- Senior Software Engineer
- CDI co-spec lead
- Red Hat, Inc.
- @antoine_sd
- www.next-presso.com
- github.com/antoinesd
| One of the most powerful feature of the CDI specification |
| Not really popularized maybe given the high level of abstraction |
| Comprehensive access to and modification of the container metadata model |
Service provider of the service javax.enterprise.inject.spi.Extension declared in META-INF/services |
| Observer pattern to listen for container initialization lifecycle events |
Injection points, parameterized types, programmatic bean
Annotated types, events, injection Targets, transactional Observers
| Open-source integration framework based on known Enterprise Integration Patterns |
| Bean binding and integration with Spring, Blueprint, Guice and CDI |
@EndpointInject(uri="jms:queue:foo")
Endpoint endpoint;
@PropertyInject(value = "timeout", defaultValue = "5000")
int timeout;
@BeanInject("foo")
FooBean foo;
@Produce(uri = "mock:foo")
ProducerTemplate producer;
@Consume(uri="jms:queue:foo")
void onFoo(@Body String body) {
...
}AnnotatedType<X>public interface AnnotatedType<X> extends Annotated {
public Class<X> getJavaClass();
public Set<AnnotatedConstructor<X>> getConstructors();
public Set<AnnotatedMethod<? super X>> getMethods();
public Set<AnnotatedField<? super X>> getFields();
}ProcessAnnotatedType<X>public interface ProcessAnnotatedType<X> {
public AnnotatedType<X> getAnnotatedType();
public void setAnnotatedType(AnnotatedType<X> type);
public void veto();
}InjectionTarget<T>public interface InjectionTarget<T> extends Producer<T> {
public void inject(T instance, CreationalContext<T> ctx);
public void postConstruct(T instance);
public void preDestroy(T instance);
}ProcessInjectionTarget<T>public interface ProcessInjectionTarget<X> {
public AnnotatedType<X> getAnnotatedType();
public InjectionTarget<X> getInjectionTarget();
public void setInjectionTarget(InjectionTarget<X> injectionTarget);
public void addDefinitionError(Throwable t);
}class CdiCamelExtension implements Extension {
Set<AnnotatedType<?>> camelBeans = new HashSet<>());
void camelAnnotations(@Observes @WithAnnotations({BeanInject.class, (1)
Consume.class, EndpointInject.class, Produce.class, PropertyInject.class})
ProcessAnnotatedType<?> pat) {
camelBeans.add(pat.getAnnotatedType());
}
<T> void camelBeansPostProcessor(@Observes ProcessInjectionTarget<T> pit) {
if (camelBeans.contains(pit.getAnnotatedType())) (2)
pit.setInjectionTarget(new CamelInjectionTarget<>(pit.getInjectionTarget()));
}
}| 1 | Detect all the types containing Camel annotations with @WithAnnotations |
| 2 | Decorate these types InjectionTarget with a custom post-processor |
class CamelInjectionTarget<T> implements InjectionTarget<T> {
InjectionTarget<T> delegate;
DefaultCamelBeanPostProcessor processor;
CamelInjectionTarget(InjectionTarget<T> target) {
delegate = target;
processor = new DefaultCamelBeanPostProcessor();
}
@Override
public void inject(T instance, CreationalContext<T> ctx) {
delegate.inject(instance, ctx);
processor.postProcessBeforeInitialization(instance); (1)
}
}| 1 | Call the Camel default bean post-processor after CDI injection |
from("jms:queue:{{input}}?transactionManager=#jtaTM")
.id("Input Consumer")
.onException().log("Rolling back message with ID ${header.JMSMessageID}")
.rollback().id("Rollback Transaction")
.end()
.log("Receiving message with ID ${header.JMSMessageID}: ${body}")
.choice()
.when(header("JMSRedelivered").isEqualTo(Boolean.TRUE))
.to("jms:queue:{{error}}?transactionManager=#jtaTM").id("Error Producer")
.otherwise()
.beanRef("transformer").id("Transformer")
.to("murex:trade-repository").id("Trade Repository")
.choice()
.when(not(isInserted))
.log("Error received: ${body}").id("Trade Repository Error")
.throwException(new CamelExecutionException("Import Failed")))
.otherwise()
.log("Answer received: ${body}").id("Trade Repository Answer");| Camel DSL Aspect Oriented Programming with CDI observer methods as pointcut and advice definitions |
void interceptProcessor(@Observes @Before @Node("foo") Exchange exchange) {
// intercept the exchange before processor with id "foo"
}void interceptProcessorBody(@Observes @Node("foo") @Body String body) {
// use Camel parameter binding annotations for the joint point context
}void receive(@Observes(during=AFTER_SUCCESS) @Endpoint("bar") Exchange exchange) {
// exchange sent to endpoint "bar" when the transaction is committed successfully
}ObserverMethod<T>public interface ObserverMethod<T> {
public Class<?> getBeanClass();
public Type getObservedType();
public Set<Annotation> getObservedQualifiers();
public Reception getReception();
public TransactionPhase getTransactionPhase();
public void notify(T event);
}ProcessObserverMethod<T, X>public interface ProcessObserverMethod<T, X> {
public AnnotatedMethod<X> getAnnotatedMethod();
public ObserverMethod<T> getObserverMethod();
public void addDefinitionError(Throwable t);
}Annotated types, alternatives, interceptors, producers
Open-source Java library providing monitoring primitives like Counter, Gauge, Histogram, Meter, timer, … |
Provides a MetricRegistry that articulates modules and reporters |
| Defines annotations for AOP frameworks like Spring AOP, AspectJ, Guice (AOP Alliance) and CDI, e.g.: |
class TimedMethodBean {
@Timed
void timedMethod() {
// Timer name => TimedMethodBean.timedMethod
}
}Bean Interfacepublic interface Bean<T> extends Contextual<T>, BeanAttributes<T> {
public Class<?> getBeanClass();
public Set<InjectionPoint> getInjectionPoints();
}
public interface Contextual<T> {
public T create(CreationalContext<T> creationalContext);
public void destroy(T instance, CreationalContext<T> creationalContext);
}
public interface BeanAttributes<T> {
public Set<Type> getTypes();
public Set<Annotation> getQualifiers();
public Class<? extends Annotation> getScope();
public String getName();
public Set<Class<? extends Annotation>> getStereotypes();
public boolean isAlternative();
}class CdiMetricsExtension implements Extension {
void defaultMetricRegistry(@Observes AfterBeanDiscovery abd, BeanManager manager) {
if (manager.getBeans(MetricRegistry.class, AnyLiteral.INSTANCE).isEmpty()) (1)
abd.addBean(new MetricRegistryBean()); (2)
}
}| 1 | Check if there is a bean of type MetricRegisty enabled |
| 2 | If any add a default MetricRegisty bean implementation |
class MetricRegistryBean implements Bean<MetricRegistry> {
@Override
public Class<? extends Annotation> getScope() {
return ApplicationScoped.class;
}
}@Inject
private Meter hits; (1)
@Timed(name = "calls") (2)
public void cachedMethod() {
if (hit) hits.mark();
}
@Produces @Metric(name = "cache-hits") (3)
private Gauge<Double> cacheHitRatioGauge(Meter hits, Timer calls) {
return () -> calls.getOneMinuteRate() == 0 ? Double.NaN :
hits.getOneMinuteRate() / calls.getOneMinuteRate();
}| 1 | Metric injection from the registry |
| 2 | Method instrumentation with CDI interceptors |
| 3 | Produce a custom Metric instance by composing others |
| Slides generated with Asciidoctor and DZSlides backend |
| Original slide template - Dan Allen & Sarah White |
| Camel CDI Extension - github.com/astefanutti/camel-cdi |
| Metrics CDI Extension - github.com/astefanutti/metrics-cdi |